﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Flux.Data.Layers;
using Flux.Data.Maps;
using Flux.Data.Tiles;
using Flux.Editor.Extensions;
using Flux.Editor.Messaging;

namespace Flux.Editor.Controls
{
	public partial class TileMapPanel
	{
		#region Fields

		private int _mouseX;
		private int _mouseY;

		#endregion

		#region Properties

		private Map _map;
		public Map Map
		{
			get { return _map; }
			set
			{
				_map = value;
			}
		}

		#endregion

		#region Initialization

		public TileMapPanel()
		{
			InitializeComponent();
			if (this.IsInDesignMode()) return;
			Map = App.SelectedMap;
			Refresh();
			App.Messenger.Register(Tokens.RefreshMap, Refresh);
		}

		#endregion

		#region Refresh / Draw

		private void Refresh(object message = null)
		{
			canvas.Children.Clear();

			if (Map == null)
			{
				return;
			}

			DrawTiles();
			DrawGrid();
			DrawCursor();
		}

		private void DrawTiles()
		{
			foreach (var layer in Map.Layers)
			{
				if (layer is TileLayer)
				{
					foreach (var tile in ((TileLayer) layer).Tiles)
					{
						DrawTile(tile);
					}
				}
			}
		}

		private void DrawTile(Tile tile)
		{
			if (tile == null) return;

			var x = tile.X;
			var y = tile.Y;
			var w = tile.Width;
			var h = tile.Height;
			var tileset = Map.GetTileset(tile.TilesetId);

			if (tileset == null)
			{
				tileset = App.Project.GetTileset(tile.TilesetId);
				Map.Tilesets.Add(tileset);
			}

			var tx = tile.Index % w;
			var ty = tile.Index / w;

			var bmp = new BitmapImage(new Uri(tileset.ImagePath, UriKind.RelativeOrAbsolute));

			var brush = new ImageBrush(bmp)
			{
				ViewboxUnits = BrushMappingMode.Absolute,
				ViewportUnits = BrushMappingMode.Absolute,
				Viewport = new Rect(0, 0, w, h),
				Viewbox = new Rect(tx * w, ty * h, w, h),
				Stretch = Stretch.None,
				TileMode = TileMode.None,
			};

			var tileCanvas = new Canvas
			{
				Width = w,
				Height = h,
				Background = brush
			};

			canvas.Children.Add(tileCanvas);
			Canvas.SetLeft(tileCanvas, x * w);
			Canvas.SetTop(tileCanvas, y * h);
		}

		private void DrawGrid()
		{
			var pw = (Map.Width * Map.TileWidth);
			var ph = (Map.Height * Map.TileHeight);

			for (var y = 0; y < ph; y += Map.TileHeight)
			{
				var line = CreateGridLine(0, y, pw, y);
				canvas.Children.Add(line);
			}

			for (var x = 0; x < pw; x += Map.TileWidth)
			{
				var line = CreateGridLine(x, 0, x, ph);
				canvas.Children.Add(line);
			}
		}

		private void DrawCursor()
		{
			var rect = new Rectangle
			{
				StrokeThickness = 1,
				Stroke = Brushes.Red,
				StrokeDashArray = new DoubleCollection { 2, 1 },
				Width = Map.TileWidth,
				Height = Map.TileHeight,
				IsHitTestVisible = false
			};
			
			canvas.Children.Add(rect);

			var mx = (_mouseX / Map.TileWidth) * Map.TileWidth;
			var my = (_mouseY / Map.TileHeight) * Map.TileHeight;
			Canvas.SetLeft(rect, mx);
			Canvas.SetTop(rect, my);
		}

		private Line CreateGridLine(double x, double y, double ex, double ey)
		{
			var line = new Line
			{
				X1 = x,
				Y1 = y,
				X2 = ex,
				Y2 = ey,
				StrokeThickness = 1,
				Stroke = Brushes.White,
				StrokeDashArray = new DoubleCollection { 2, 1 },
				IsHitTestVisible = false
			};
			return line;
		}

		#endregion

		#region Methods
		
		#endregion

		#region Events

		private void CanvasOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			if (App.SelectedLayer == null) return;

			var pos = e.GetPosition((IInputElement)sender);
			_mouseX = (int)pos.X;
			_mouseY = (int)pos.Y;
			var mx = (_mouseX / Map.TileWidth) * Map.TileWidth;
			var my = (_mouseY / Map.TileHeight) * Map.TileHeight;
			var tx = (mx / Map.TileWidth);
			var ty = (my / Map.TileHeight);

			var tile = new Tile
			{
				X = tx,
				Y = ty,
				Width = Map.TileWidth,
				Height = Map.TileHeight,
				TilesetId = App.SelectedTileset.Id,
				Index = App.SelectedLeftTile
			};

			var tileLayer = (TileLayer)App.SelectedLayer; //((TileLayer) Map.Layers[0]);
			tileLayer.Add(tile);
		}

		private void CanvasOnMouseRightButtonDown(object sender, MouseButtonEventArgs e)
		{
			if (App.SelectedLayer == null) return;

			var pos = e.GetPosition((IInputElement)sender);
			_mouseX = (int)pos.X;
			_mouseY = (int)pos.Y;
			var mx = (_mouseX / Map.TileWidth) * Map.TileWidth;
			var my = (_mouseY / Map.TileHeight) * Map.TileHeight;
			var tx = (mx / Map.TileWidth);
			var ty = (my / Map.TileHeight);

			var tile = new Tile
			{
				X = tx,
				Y = ty,
				Width = Map.TileWidth,
				Height = Map.TileHeight,
				TilesetId = App.SelectedTileset.Id,
				Index = App.SelectedRightTile
			};

			var tileLayer = (TileLayer) App.SelectedLayer; //((TileLayer)Map.Layers[0]);
			tileLayer.Add(tile);
		}

		private void CanvasOnMouseMove(object sender, MouseEventArgs e)
		{
			var pos = e.GetPosition((IInputElement)sender);
			_mouseX = (int)pos.X;
			_mouseY = (int)pos.Y;

			if (e.LeftButton == MouseButtonState.Pressed)
			{
				var eventArgs = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left);
				CanvasOnMouseLeftButtonDown(sender, eventArgs);
			}

			if (e.RightButton == MouseButtonState.Pressed)
			{
				var eventArgs = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left);
				CanvasOnMouseRightButtonDown(sender, eventArgs);
			}

			Refresh();
		}

		#endregion
	}
}
